/*
 * PSAnalyzer.cpp
 *
 *  Created on: 2015年12月23日
 *      Author: terry
 */

#include "PSAnalyzer.h"
#include "ByteHelper.h"
#include "BasicMacro.h"
#include "H264NaluParser.h"
#include "TStringCast.h"


namespace av
{

#ifndef MAKEFOURCC
typedef unsigned long DWORD;
typedef unsigned char BYTE;
#define MAKEFOURCC(c0,c1,c2,c3) ((DWORD)(BYTE)(c0)|((DWORD)(BYTE)(c1)<<8)|((DWORD)(BYTE)(c2)<<16)|((DWORD)(BYTE)(c3)<<24))
#endif // MAKEFOURCC

#ifndef MAKEWORD
#define MAKEWORD(a,b) ((uint16_t) (((uint8_t) (a)) | ((uint16_t) ((uint8_t) (b))) << 8))
#endif //MAKEWORD

/// 大华流头2个包, 开始的4字节
static const uint32_t	HVAG = MAKEFOURCC('H', 'V', 'A', 'G');
/// 大华流后续的mark=1的包, 开始的4字节
static const uint32_t	DHAV = MAKEFOURCC('D', 'H', 'A', 'V');

/// 海康流头一个包, 开始的4字节
static const uint32_t	IMKH = MAKEFOURCC('I', 'M', 'K', 'H');

//static const uint32_t  PS_PREFIX = MAKEFOURCC(0, 0, 1, 0xba);
//static const uint32_t  SH_PREFIX = MAKEFOURCC(0, 0, 1, 0xbb);
//static const uint32_t  PSM_PREFIX = MAKEFOURCC(0, 0, 1, 0xbc);

static const uint8_t  START_CODE[] = {0, 0, 1};
static const uint8_t  LONG_START_CODE[] = {0, 0, 0, 1};


PSAnalyzer::PSAnalyzer():
		m_provider(PSType::kProviderNone),
		m_format(PSType::kUnknownFormat),
		m_codec(MEDIA_CODEC_NONE)
{
}

PSAnalyzer::~PSAnalyzer()
{
}

bool PSAnalyzer::analyze(const RtpPacket& pkt)
{
	if (pkt.size < (int)sizeof(uint32_t))
	{
		return false;
	}

	uint32_t code = *(uint32_t*)pkt.data;
	if (code == DHAV)
	{
		m_provider = PSType::kProviderDahua;

#ifdef WIN32
        /// windows 下, 当做大华私有流处理
        m_format = PSType::kDhavFormat;
        m_codec = MEDIA_CODEC_DHAV;
#else
        m_format = PSType::kDahuaFormat;
        m_codec = MEDIA_CODEC_H264;
#endif //

	}
	else if (code == HVAG)
	{
		m_provider = PSType::kProviderDahua;

#ifdef WIN32
		/// windows 下, 当做大华私有流处理
        m_format = PSType::kDhavFormat;
		m_codec = MEDIA_CODEC_DHAV;
#else
        m_format = PSType::kDahuaFormat;
		m_codec = MEDIA_CODEC_H264;
#endif //
	}
	else if (code == IMKH)
	{
		m_provider = PSType::kProviderHK;
	}
	else if (ByteHelper::startsWith(pkt.data, pkt.size, START_CODE, sizeof(START_CODE)))
	{
		/// 以 0 0 1 开头
		uint8_t type = pkt.data[3];
		if (type == 0xBA)
		{
			m_format = PSType::kFormalFormat;
			parsePSPacket(pkt, m_codec);
		}
		else if (type == 0xBC)
		{
			m_format = PSType::kFormalFormat;
			parsePSMPacket(pkt, m_codec);
		}
		else if ((type >= 0xE0) && (type <= 0xEF))
		{
			/// video
			m_format = PSType::kFormalFormat;
			parseVideoPES(pkt, m_codec);
		}
		else if (type >= 0xBA && type <= 0xBF)
		{
			/// PS header
			m_format = PSType::kFormalFormat;
		}
		else if (type >= 0xC0 && type <= 0xDF)
		{
			m_format = PSType::kFormalFormat;
		}
		else
		{
            NaluPacket nalu;
            H264NaluParser::parseNalu(pkt.data, pkt.size, nalu);
            if (nalu.type == NaluPacket::NALU_SPS)
            {
                m_codec = MEDIA_CODEC_H264;
            }
		}
	}
    else if (ByteHelper::startsWith(pkt.data, pkt.size, LONG_START_CODE, sizeof(LONG_START_CODE)))
    {
        NaluPacket nalu;
        H264NaluParser::parseNalu(pkt.data, pkt.size, nalu);
        if (nalu.type == NaluPacket::NALU_SPS)
        {
            m_codec = MEDIA_CODEC_H264;
        }
    }

	return isReady();
}

PSType::Provider PSAnalyzer::getProvider() const
{
	return m_provider;
}

PSType::PacketFormat PSAnalyzer::getPacketFormat() const
{
	return m_format;
}

MediaCodec PSAnalyzer::getCodec()
{
	return m_codec;
}

bool PSAnalyzer::isReady()
{
	return (m_format != PSType::kUnknownFormat) && (m_codec != MEDIA_CODEC_NONE);
}

void PSAnalyzer::reset()
{
	m_provider = PSType::kProviderNone;
	m_format = PSType::kUnknownFormat;
	m_codec = MEDIA_CODEC_NONE;
}

bool PSAnalyzer::parseVideoPES(const RtpPacket& pkt, MediaCodec& codec)
{
	bool found = false;

	uint8_t* data = pkt.data;
	size_t size = pkt.size;

	int pesPktLength = MAKEWORD(data[5], data[4]);
	int pesHeadLength = data[8];

	int headSize = 8 + 1 + pesHeadLength;
	if (pkt.size > (headSize + 4))
	{
		if (ByteHelper::startsWith(data + headSize, size - headSize, START_CODE, sizeof(START_CODE)))
		{
			codec = MEDIA_CODEC_H264;
			setIfNone(m_provider, PSType::kProviderHK);
			found = true;
		}
		else
		{
			headSize += 0x14;	/// 大华14字节私有头
			if (ByteHelper::startsWith(data + headSize, size - headSize, START_CODE, sizeof(START_CODE)))
			{
				codec = MEDIA_CODEC_H264;
				setIfNone(m_provider, PSType::kProviderDahua);
				found = true;
			}
		}
	}

	if (!found)
	{
		/// 只要包含 0 0 1 就认为是H.264 ?
		size_t idx = ByteHelper::find(pkt.data, pkt.size, sizeof(START_CODE),
				START_CODE, sizeof(START_CODE));
		if (idx != (size_t)-1)
		{
			codec = MEDIA_CODEC_H264;
			found = true;
		}
	}

	return found;
}

void PSAnalyzer::setIfNone(PSType::Provider& provider, PSType::Provider value)
{
	if (provider == PSType::kProviderNone)
	{
		provider = PSType::kProviderDahua;
	}
}

bool PSAnalyzer::parsePSPacket(const RtpPacket& pkt, MediaCodec& codec)
{
	bool found = false;
	size_t start = 0;
	size_t idx = ByteHelper::find(pkt.data, pkt.size, start, START_CODE, sizeof(START_CODE));
	while (idx != (size_t)-1)
	{
		RtpPacket psPacket(pkt);
		psPacket.data += idx;
		psPacket.size -= idx;
		if (psPacket.size > (int)sizeof(uint32_t))
		{
			uint8_t type = psPacket.data[3];
			if (type == 0xBC)
			{
				found = parsePSMPacket(psPacket, codec);
				if (found)
				{
					break;
				}
			}
			else if ((type >= 0xE0) && (type <= 0xEF))
			{
				found = parseVideoPES(psPacket, codec);
				if (found)
				{
					break;
				}
			}
		}

		start = idx + sizeof(START_CODE);
		idx = ByteHelper::find(pkt.data, pkt.size, start, START_CODE, sizeof(START_CODE));
	}
	return found;
}


namespace detail
{

struct IndexByte
{
    int index;
    uint8_t value;
};

IndexByte s_hk_private[] = 
{
    {0x11, 0xC9},
    {0x30, 0xB0},
    {0x44, 0x92},
    {0x4e, 0xF0},
    {0x50, 0x01},
    {0x51, 0xA0}
};

IndexByte s_hk_ps[] = 
{
    {0x11, 0x02},
    {0x30, 0x1B},
    {0x44, 0x90},
    {0x4e, 0x7D},
    {0x50, 0x03},
    {0x51, 0xE8}
};


double computeSimilarity(uint8_t* data, size_t size, IndexByte* bytes, size_t count)
{
    double scale = 0;
    for (size_t i = 0; i < count; ++ i)
    {
        IndexByte indexByte = bytes[i];
        uint8_t theByte = data[indexByte.index];
        if ((theByte & indexByte.value) == indexByte.value)
        {
            scale += 1;
        }
    }
    return scale / count;
}


}

bool PSAnalyzer::parsePSMPacket(const RtpPacket& pkt, MediaCodec& codec)
{
	if (pkt.size < (0x005A + 4))
	{
		return false;
	}

	bool found = false;
	int pesPktLength = MAKEWORD(pkt.data[5], pkt.data[4]);
	if (pesPktLength == 0x005A)
	{
        /// 序号   私有流   PS流
        /// 0x11   0xc9     0x02 
        /// 0x30   0xB0     0x1B
        /// 0x44   0x92     0x90
        /// 0x4e   0xF0     0x7D
        /// 0x50   0x01     0x03
        /// 0x51   0xA0     0xE8
        /// 

        double privateScale = detail::computeSimilarity(pkt.data, pkt.size,
            detail::s_hk_private, ARRAY_SIZE(detail::s_hk_private));

        double psScale = detail::computeSimilarity(pkt.data, pkt.size,
            detail::s_hk_ps, ARRAY_SIZE(detail::s_hk_ps));
        
        if (privateScale > psScale)
        {
            m_provider = PSType::kProviderHK;
            m_codec = MEDIA_CODEC_HK;
            found = true;
        }
        else
        {
            m_provider = PSType::kProviderHK;
            m_codec = MEDIA_CODEC_H264;
            found = true;
        }
	}
	return found;
}



} /* namespace av */
